package org.colafries.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.colafries.common.assertion.Asserts;
import org.colafries.admin.entity.domain.CustomQueryParam;
import org.colafries.admin.entity.domain.CustomTable;
import org.colafries.admin.entity.domain.DataModel;
import org.colafries.admin.entity.domain.DataModelConfig;
import org.colafries.admin.entity.vo.ColumnRspVO;
import org.colafries.admin.entity.vo.CustomQueryReqVO;
import org.colafries.admin.entity.vo.CustomQueryRspVO;
import org.colafries.admin.entity.vo.CustomTableReqVO;
import org.colafries.admin.exceptions.enums.ErrorCodeEnum;
import org.colafries.admin.querybuilder.customQuery.CustomQuerySqlSegmentBuilder;
import org.colafries.admin.querybuilder.customQuery.*;
import org.colafries.admin.querybuilder.entity.CustomQueryParamContext;
import org.colafries.admin.querybuilder.entity.CustomQuerySqlBuildContext;
import org.colafries.admin.querybuilder.utils.SqlSegmentBuilderUtil;
import org.colafries.admin.service.*;
import org.colafries.admin.wrapper.Response;
import org.colafries.admin.wrapper.ResponseWrapper;
import org.colafries.common.enums.ColumnDataTypeEnum;
import org.colafries.admin.entity.domain.QueryParam;
import org.colafries.admin.querybuilder.entity.SelectSqlSchema;
import org.colafries.common.enums.Flag;
import org.colafries.metadata.result.JdbcSelectResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author colafries
 * @version v1.0
 * @ClassName ICustomQueryServiceImpl
 * @Description
 * @date Created in 2024/4/22
 **/
@Service
@Slf4j
public class ICustomQueryServiceImpl implements ICustomQueryService {

    @Resource
    IDataModelService iDataModelService;
    @Resource
    IDataBaseService iDataBaseService;
    @Resource
    ICustomTableService iCustomTableService;
    @Resource
    ICustomQueryParamService iCustomQueryParamService;

    @Resource
    IDataModelConfigService iDataModelConfigService;

    /**
     * 查询模型结果
     *
     * @param customQueryReqVO
     * @return org..colafries.admin.metadata.result.JdbcResult
     * @author colafries
     * @Date 2024/3/25
     */
    @Override
    public Response<?> query(CustomQueryReqVO customQueryReqVO) {
        //获取数据模型
        DataModel dataModel = iDataModelService.getOne(
                Wrappers.<DataModel>lambdaQuery().eq(DataModel::getId, customQueryReqVO.getModelId())
        );
        //获取模型配置
        if (Asserts.isNull(dataModel)) {
            return ResponseWrapper.resp(ErrorCodeEnum.GL999999.code(), "模型不存在，模型id " + customQueryReqVO.getModelId());
        }
        List<DataModelConfig> dataModelConfigs = iDataModelConfigService.lambdaQuery()
                .eq(DataModelConfig::getDataModelId, dataModel.getId())
                .list();
        if (Asserts.isNullCollection(dataModelConfigs)) {
            return ResponseWrapper.resp(ErrorCodeEnum.GL999999.code(), "模型配置为空，模型id " + dataModel.getId());
        }
        //构建查询参数(queryParam)
        QueryParam queryParam = buildQueryParam(customQueryReqVO.getQueryParam(), dataModelConfigs);
        //构建模型SQL
        String querySql = buildQuerySql(dataModel.getId(), queryParam);
        // 查询条数限制
        int size = calculateQuerySize(customQueryReqVO);
        JdbcSelectResult jdbcSelectResult = iDataBaseService.querySqlPage(dataModel.getDatabaseId(), querySql, customQueryReqVO.getCurrent(), size);
        // 查询结果列
        List<ColumnRspVO> columnRspVOs = buildColumnRspVOList(jdbcSelectResult, queryParam, dataModelConfigs);
        // 查询结果
        CustomQueryRspVO customQueryRspVO = new CustomQueryRspVO();
        customQueryRspVO.setModelId(dataModel.getId())
                .setCustomQuerySql(jdbcSelectResult.getQuerySql())
                .setColumns(columnRspVOs)
                .setRows(jdbcSelectResult.getRowData())
                .setExecTime(jdbcSelectResult.getExecTime())
                .setCurrent(jdbcSelectResult.getPage())
                .setSize(jdbcSelectResult.getSize())
                .setTotal(jdbcSelectResult.getTotal());

        // 根据查询配置，对查询结果进行处理
        processResultBasedOnConfig(customQueryRspVO, customQueryReqVO.getQueryConfig());
        return ResponseWrapper.ok(customQueryRspVO);
    }


    /**
     * 保存自定义查询参数
     *
     * @param customQueryReqVO
     * @return int
     * @author colafries
     * @Date 2024/4/13
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveCustomQueryParam(CustomQueryReqVO customQueryReqVO) {
        // add new CustomQueryParam
        CustomQueryParam customQueryParam = new CustomQueryParam();
        customQueryParam.setDataModelId(customQueryReqVO.getModelId());
        //构建查询参数(queryParam)
        JSONObject queryParamObject = (JSONObject) JSONObject.toJSON(customQueryReqVO.getQueryParam());
        //移除 conditions filters
        queryParamObject.remove("conditions");
        queryParamObject.remove("filters");

        customQueryParam.setQueryParamJson(JSONObject.toJSONString(queryParamObject));
        //TODO UserId
        customQueryParam.setQueryUserId(1);
        //自定义查询参数备注
        customQueryParam.setRemark(customQueryReqVO.getRemark());
        return iCustomQueryParamService.save(customQueryParam);
    }

    /**
     * 获取最新保存自定义查询参数
     *
     * @param customQueryReqVO
     * @return org..colafries.admin.entity.domain.CustomQueryParam
     * @author colafries
     * @Date 2024/4/14
     */
    @Override
    public CustomQueryParam getLatestCustomQueryParam(CustomQueryReqVO customQueryReqVO) {
        return iCustomQueryParamService.lambdaQuery()
                .eq(CustomQueryParam::getDataModelId, customQueryReqVO.getModelId())
                .eq(CustomQueryParam::getQueryUserId, customQueryReqVO.getQueryUserId())
                .orderByDesc(CustomQueryParam::getUpdateTime)
                .last("LIMIT 1").one();
    }

    /**
     * 获取所有已保存自定义查询参数
     *
     * @param customQueryReqVO
     * @return java.util.List<org..colafries.admin.entity.domain.CustomQueryParam>
     * @author colafries
     * @Date 2024/5/20
     */
    @Override
    public List<CustomQueryParam> getAllQueryParam(CustomQueryReqVO customQueryReqVO) {
        return iCustomQueryParamService.lambdaQuery()
                .eq(CustomQueryParam::getDataModelId, customQueryReqVO.getModelId())
                .eq(CustomQueryParam::getQueryUserId, customQueryReqVO.getQueryUserId())
                .orderByDesc(CustomQueryParam::getUpdateTime)
                .list();
    }

    /**
     * 根据查询配置对查询结果进行处理
     *
     * @param queryConfig      查询配置
     * @param customQueryRspVO 查询结果
     */
    private void processResultBasedOnConfig(CustomQueryRspVO customQueryRspVO, CustomQueryReqVO.QueryConfig queryConfig) {
        if (Asserts.isNotNull(queryConfig)) {
            //是否返回列
            if (Asserts.isNotNull(queryConfig.getColumnsReturnFlag())) {
                if (queryConfig.getColumnsReturnFlag().equals(Flag.NO)) {
                    customQueryRspVO.setColumns(Lists.newArrayList());
                }
            }
            // 是否返回SQL
            if (Asserts.isNotNull(queryConfig.getSqlReturnFlag())) {
                if (queryConfig.getSqlReturnFlag().equals(Flag.NO)) {
                    customQueryRspVO.setCustomQuerySql(StringUtils.EMPTY);
                }
            }
            // 是否分页
            if (Asserts.isNotNull(queryConfig.getPageFlag())) {
                if (queryConfig.getPageFlag().equals(Flag.NO)) {
                    customQueryRspVO.setCurrent(null);
                    customQueryRspVO.setTotal(null);
                }
            }
        }
    }

    /**
     * 构建查询结果列集合
     *
     * @param jdbcSelectResult       jdbc查询结果
     * @param queryParam       查询参数
     * @param dataModelConfigs 数据模型配置
     * @return java.util.List<org..colafries.admin.entity.vo.ColumnRspVO>
     * @author colafries
     * @Date 2024/5/26
     */
    private List<ColumnRspVO> buildColumnRspVOList(JdbcSelectResult jdbcSelectResult, QueryParam queryParam, List<DataModelConfig> dataModelConfigs) {
        //构建查询参数结果列Map key：结果列hash 方便查询
        Map<String, QueryParam.ResultColumn> resultColumnMap = queryParam.getResultColumns().stream()
                .collect(Collectors.toMap(QueryParam.ResultColumn::getFieldHash, Function.identity()));
        //构建数据模型Map key：模型配置列hash 方便查询
        Map<String, DataModelConfig> dataModelConfigMap = dataModelConfigs.stream()
                .collect(Collectors.toMap(DataModelConfig::getFieldHash, Function.identity()));
        //根据数据库查询结果，构建模型查询列
        return jdbcSelectResult.getColumns().stream()
                // column.getName() ,例如；COLG7WY1WB8AOR 即dataModelConfig.fieldHash
                .map(column -> Optional.ofNullable(dataModelConfigMap.get(column.getName()))
                        .map(modelConfig -> {
                            QueryParam.ResultColumn resultColumn = resultColumnMap.get(modelConfig.getFieldHash());
                            if (Asserts.isNull(resultColumn)) {
                                return null;
                            }
                            return ColumnRspVO.builder()
                                    .field(modelConfig.getField())
                                    .dbType(column.getType())
                                    .alias(modelConfig.getAlias())
                                    .javaType(ColumnDataTypeEnum.get(modelConfig.getDataType().getCode()).toString())
                                    .fieldHash(modelConfig.getFieldHash())
                                    .tableHash(resultColumn.getTableHash())
                                    .type(resultColumn.getColumnType())
                                    .defaultAggregator(resultColumn.getDefaultAggregator())
                                    .build();
                        }).orElse(null)
                ).collect(Collectors.toList());
    }

    private int calculateQuerySize(CustomQueryReqVO customQueryReqVO) {
        int size = 0;
        CustomQueryReqVO.QueryConfig queryConfig = customQueryReqVO.getQueryConfig();
        if (Asserts.isNotNull(queryConfig)) {
            if (Asserts.isNotNull(queryConfig.getPageFlag())) {
                if (queryConfig.getPageFlag().equals(Flag.NO)) {
                    // 如果不分页
                    // 如果自定义查询参数中指定了limit，则按照limit返回数据，否则查询全部
                    size = queryConfig.getLimit() == null ? -1 : queryConfig.getLimit();
                }
                if (queryConfig.getPageFlag().equals(Flag.YES)) {
                    // 如果分页
                    // 如果自定义查询参数中指定了limit，则按照limit返回数据，否则按照分页查询
                    size = queryConfig.getLimit() == null ? customQueryReqVO.getSize() : queryConfig.getLimit();
                }
            }
        }
        return size;
    }

    /**
     * 构建SQL查询参数
     *
     * @param customQueryParam
     * @param dataModelConfigs
     * @return org..colafries.common.model.metadata.QueryParam
     * @author colafries
     * @Date 2024/5/16
     */
    private QueryParam buildQueryParam(CustomQueryReqVO.CustomQueryParam customQueryParam, List<DataModelConfig> dataModelConfigs) {
        try {
            if (Asserts.isNullCollection(dataModelConfigs)) {
                throw new RuntimeException("构建查询参数异常，模型配置为空");
            }
            CustomQueryParamContext customQueryParamContext = new CustomQueryParamContext();
            customQueryParamContext.setDataModelConfigList(dataModelConfigs);
            //构建dataMap
            for (DataModelConfig config : dataModelConfigs) {
                customQueryParamContext.getDataModelConfigMap().put(config.getFieldHash(), config);
                customQueryParamContext.getDataModelListMap()
                        .computeIfAbsent(config.getType().getValue(), new Function<String, List<DataModelConfig>>() {
                            @Override
                            public List<DataModelConfig> apply(String s) {
                                return new ArrayList<>();
                            }
                        })
                        .add(config);
            }
            // SqlSegmentBuilder List
            List<CustomQuerySqlSegmentBuilder> customQuerySqlSegmentBuilders = Arrays.asList(
                    new CustomQueryDimensionBuilder(),
                    new CustomQueryConditionBuilder(),
                    new CustomQueryMeasureBuilder(),
                    new CustomQuerySortBuilder(),
                    new CustomQueryFilterBuilder()
            );
            // SqlSegmentBuilder start to build
            customQuerySqlSegmentBuilders.forEach(builder -> builder.buildQueryParma(customQueryParam, customQueryParamContext));

            return QueryParam.builder()
                    .dimensions(customQueryParamContext.getDimensions())
                    .measures(customQueryParamContext.getMeasures())
                    .conditions(customQueryParamContext.getConditions())
                    .filters(customQueryParamContext.getFilters())
                    .sorts(customQueryParamContext.getSorts())
                    .build();
        } catch (Exception e) {
            log.warn("构建查询参数异常 异常信息：" + e.getMessage());
            throw new RuntimeException("构建查询参数异常 异常信息：" + e.getMessage());
        }
    }

    /**
     * 构建查询SQL
     *
     * @param modelId    模型id
     * @param queryParam 查询参数
     * @return java.lang.String
     * @author colafries
     * @Date 2024/5/16
     */
    private String buildQuerySql(Integer modelId, QueryParam queryParam) {
        //获取模型自定义Table
        List<CustomTable> customTables = iCustomTableService.lambdaQuery().eq(CustomTable::getDataModelId, modelId).list();
        if (CollectionUtil.isEmpty(customTables)) {
            log.info("自定义查询不存在 模型id {}", modelId);
            return null;
        }
        // 获取自定义Table tableSchema
        Map<String, ColumnRspVO> customTableSchemaMap = getTableSchemaMap(modelId, customTables);
        CustomQuerySqlBuildContext customQuerySqlQueryContext = getSqlQueryContext(queryParam, customTableSchemaMap);

        // queryParam 绑定查询结果列
        queryParam.setResultColumns(customQuerySqlQueryContext.getResultColumns());

        SelectSqlSchema selectSqlSchema = SqlSegmentBuilderUtil.buildSelectSqlSchema(customQuerySqlQueryContext, customTables);
        return SqlSegmentBuilderUtil.buildFinalSql(selectSqlSchema);
    }

    /**
     * 获取SQL查询上下文
     *
     * @param queryParam
     * @param customTableSchemaMap
     * @return org..colafries.admin.querybuilder.entity.SqlQueryContext
     * @author colafries
     * @Date 2024/5/17
     */
    private CustomQuerySqlBuildContext getSqlQueryContext(QueryParam queryParam, Map<String, ColumnRspVO> customTableSchemaMap) {
        CustomQuerySqlBuildContext customQuerySqlQueryContext = new CustomQuerySqlBuildContext();
        customQuerySqlQueryContext.setCustomTableSchemaMap(customTableSchemaMap);
        // SqlSegmentBuilder List
        List<CustomQuerySqlSegmentBuilder> customQuerySqlSegmentBuilders = Arrays.asList(
                new CustomQueryDimensionBuilder(),
                new CustomQueryConditionBuilder(),
                new CustomQueryMeasureBuilder(),
                new CustomQuerySortBuilder(),
                new CustomQueryFilterBuilder()
        );
        // SqlSegmentBuilder start to build
        customQuerySqlSegmentBuilders.forEach(builder -> builder.buildQuery(queryParam, customQuerySqlQueryContext));
        return customQuerySqlQueryContext;
    }

    /**
     * 根据模型
     *
     * @param modelId
     * @param customTables
     * @return java.util.Map<java.lang.String, org..colafries.admin.entity.vo.ColumnRspVO>
     * @author colafries
     * @Date 2024/5/11
     */
    private Map<String, ColumnRspVO> getTableSchemaMap(Integer modelId, List<CustomTable> customTables) {
        List<ColumnRspVO> columnRspVOList = new ArrayList<>();
        customTables.forEach(customTable -> {
            // 获取模型的自定义表的tableSchemas
            List<ColumnRspVO> customTableSchemas = iDataModelService.getTableSchema(
                    new CustomTableReqVO().setDataModelId(modelId).setTableId(customTable.getId())
            );
            // 自定义表tableSchemas中补充TableHash
            List<ColumnRspVO> customTableSchemas1 = customTableSchemas.stream()
                    .peek(columnRspVO -> columnRspVO.setTableHash(customTable.getTableHash()))
                    .collect(Collectors.toList());

            columnRspVOList.addAll(customTableSchemas1);
        });
        return columnRspVOList.stream()
                .collect(Collectors.toMap(ColumnRspVO::getFieldHash, Function.identity()));
    }

}
